import md5 from 'js-md5'

function getError(action, option, xhr) {
  let msg
  if (xhr.response) {
    msg = `${xhr.response.error || xhr.response}`
  } else if (xhr.responseText) {
    msg = `${xhr.responseText}`
  } else {
    msg = `fail to post ${action} ${xhr.status}`
  }

  const err = new Error(msg)
  err.status = xhr.status
  err.method = 'post'
  err.url = action
  return err
}

function getBody(xhr) {
  const text = xhr.responseText || xhr.response
  if (!text) {
    return text
  }

  try {
    return JSON.parse(text)
  } catch (e) {
    return text
  }
}

function uploadChunk(fileMD5, option) {
  if (typeof XMLHttpRequest === 'undefined') {
    return
  }

  const xhr = new XMLHttpRequest()
  const action = option.action

  if (xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
      if (e.total > 0) {
        e.percent = e.loaded / e.total * 100
      }
      option.onChunkProgress(e)
    }
  }

  const formData = new FormData()

  option.data.guid = fileMD5
  if (option.data) {
    Object.keys(option.data).forEach(key => {
      formData.append(key, option.data[key])
    })
  }
  // console.log(option)
  formData.append(option.filename, option.file, option.file.name)

  xhr.onerror = function error(e) {
    option.onChunkError(e)
  }

  xhr.onload = function onload() {
    if (xhr.status < 200 || xhr.status >= 300) {
      return option.onChunkError(getError(action, option, xhr))
    }

    option.onChunkSuccess(getBody(xhr))
  }

  xhr.open('post', action, true)

  if (option.withCredentials && 'withCredentials' in xhr) {
    xhr.withCredentials = true
  }

  const headers = option.headers || {}

  for (const item in headers) {
    if (headers.hasOwnProperty(item) && headers[item] !== null) {
      xhr.setRequestHeader(item, headers[item])
    }
  }
  xhr.send(formData)
  return xhr
}

const getChunkInfo = (file, currentChunk, chunkSize) => {
  // console.log(chunkSize)
  const start = (currentChunk - 1) * chunkSize
  const end = Math.min(file.size, start + chunkSize)
  const chunk = file.slice(start, end)
  return { start, end, chunk }
}

const maxRetryCount = 3 // 最大重试次数

/**
 * 上传分片
 */
const uploadOne = (fileMD5, file, option, currentChunk = 1, retryCount = 0) => {
  // console.log(option.data)
  // 分片
  const chunkSize = 1 * 1024 * 1024 // 2MB一片
  const chunkCount = Math.ceil(file.size / chunkSize) // 总片数

  // 针对单个文件进行chunk上传
  const { chunk } = getChunkInfo(file, currentChunk, chunkSize)
  option.data.chunk = currentChunk
  option.data.chunks = chunkCount
  // console.log(file)
  option.file = chunk
  option.file.uid = file.uid
  option.file.name = file.name
  option.file.status = 'ready'
  option.onChunkSuccess = (response) => {
    // 分片上传完成
    if (currentChunk >= chunkCount) {
      return option.onSuccess(response)
    }
    return uploadOne(fileMD5, file, option, currentChunk + 1)
  }
  option.onChunkProgress = (e) => {
    if (e.total > 0) {
      const loaded = chunkSize * currentChunk
      e.allTotal = file.size
      e.allLoaded = loaded + e.loaded * (file.size - loaded > chunkSize ? chunkSize : file.size - loaded) / e.total
      e.percent = e.allLoaded / e.allTotal * 100
      e.percent = e.percent > 100 ? 100 : e.percent
    } else {
      e.percent = 0
    }
    // console.log(e)
    option.onProgress(e)
  }
  option.onChunkError = (e) => {
    if (retryCount >= maxRetryCount) {
      // 异常
      return option.onError(e)
    } else {
      return uploadOne(fileMD5, file, option, currentChunk, ++retryCount)
    }
  }
  return uploadChunk(fileMD5, option)
}

const upload = (option) => {
  const fileRederInstance = new FileReader()
  fileRederInstance.readAsBinaryString(option.file)
  fileRederInstance.addEventListener('load', e => {
    const fileMD5 = md5(e.target.result)
    // console.log(fileMD5)
    // option.data.guid = fileMD5
    // console.log(option)
    uploadOne(fileMD5, option.file, option, 1)
  })
}

export default upload
